package com.graphqljava.demo.gql.gql;

import com.google.common.base.Charsets;
import com.google.common.io.Resources;
import com.graphqljava.demo.gql.gql.fetchers.GlxyGraphQLDataFetchers;
import com.graphqljava.demo.gql.gql.fetchers.InMemoryGraphQLDataFetchers;
import graphql.GraphQL;
import graphql.schema.GraphQLSchema;
import graphql.schema.idl.RuntimeWiring;
import graphql.schema.idl.SchemaGenerator;
import graphql.schema.idl.SchemaParser;
import graphql.schema.idl.TypeDefinitionRegistry;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.net.URL;

import static graphql.schema.idl.TypeRuntimeWiring.newTypeWiring;

/**
 * com.graphqljava.demo.gql
 *
 * @author Oscar.Chen
 * @date 6/15/2020 12:17 PM
 */
@Component
public class GraphQLProvider {

    private GraphQL graphQL;

    final InMemoryGraphQLDataFetchers inMemoryGraphQLDataFetchers;
    final GlxyGraphQLDataFetchers glxyGraphQLDataFetchers;

    public GraphQLProvider(InMemoryGraphQLDataFetchers inMemoryGraphQLDataFetchers,
                           GlxyGraphQLDataFetchers glxyGraphQLDataFetchers) {
        this.inMemoryGraphQLDataFetchers = inMemoryGraphQLDataFetchers;
        this.glxyGraphQLDataFetchers = glxyGraphQLDataFetchers;
    }

    @Bean
    public GraphQL getGraphQL() {
        return graphQL;
    }

    /**
     * 修改此处资源文件使用不同的 SDL 定义
     */
    @Value("${graphql.schema}")
    String resource;

    @PostConstruct
    public void init() throws IOException {
        URL url = Resources.getResource(resource);
        String sdl = Resources.toString(url, Charsets.UTF_8);
        GraphQLSchema graphQLSchema = buildSchema(sdl);
        this.graphQL = GraphQL.newGraphQL(graphQLSchema).build();
    }

    private GraphQLSchema buildSchema(String sdl) {
        TypeDefinitionRegistry typeDefinitionRegistry = new SchemaParser().parse(sdl);

        RuntimeWiring runtimeWiring;

        if (resource.contains("glxy")) {
            runtimeWiring = buildWiring();
        } else {
            runtimeWiring = buildInMemoryWiring();
        }

        SchemaGenerator schemaGenerator = new SchemaGenerator();
        return schemaGenerator.makeExecutableSchema(typeDefinitionRegistry, runtimeWiring);
    }

    /**
     * 从 mongodb 中加载测试数据
     */
    private RuntimeWiring buildWiring() {
        return RuntimeWiring.newRuntimeWiring()
            .type(newTypeWiring("Query").dataFetcher("achieveById", glxyGraphQLDataFetchers.getAchieveByIdDataFetcher()))
            .type(newTypeWiring("Query").dataFetcher("personById", glxyGraphQLDataFetchers.getPersonByIdDataFetcher()))
            .type(newTypeWiring("Query").dataFetcher("allPerson", glxyGraphQLDataFetchers.getAllPersonDataFetcher()))
            .type(newTypeWiring("Query").dataFetcher("allAchieve", glxyGraphQLDataFetchers.getAllAchieveDataFetcher()))
            .type(newTypeWiring("Query").dataFetcher("personByPersonId", glxyGraphQLDataFetchers.getPersonByPersonIdDataFetcher()))
            .type(newTypeWiring("Query").dataFetcher("personByProjectId", glxyGraphQLDataFetchers.getPersonByProjectIdDataFetcher()))
            .type(newTypeWiring("Achieve").dataFetcher("persons", glxyGraphQLDataFetchers.getPersonDataFetcher()))
            .type(newTypeWiring("Person").dataFetcher("achieve", glxyGraphQLDataFetchers.getAchieveDataFetcher()))
            .build();
    }

    /**
     * 从内存中加载测试数据
     */
    private RuntimeWiring buildInMemoryWiring() {
        return RuntimeWiring.newRuntimeWiring()
            .type(newTypeWiring("Query").dataFetcher("postById", inMemoryGraphQLDataFetchers.getPostByIdDataFetcher()))
            .type(newTypeWiring("Query").dataFetcher("userById", inMemoryGraphQLDataFetchers.getUserByIdDataFetcher()))
            .type(newTypeWiring("Post").dataFetcher("user", inMemoryGraphQLDataFetchers.getUserDataFetcher()))
            .type(newTypeWiring("User").dataFetcher("posts", inMemoryGraphQLDataFetchers.getPostDataFetcher()))
            .build();
    }
}
